﻿// <copyright file="OrthogonalComposite.cs" company="graphs-dotnet">
// Copyright 2009 Dmitri Pavlenkov
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// THIRD PARTY LICENSES
//
// This file's code annotations follow David Betz' Code Annotation Format.
// The orignal can be obtained at
//
// http://www.netfxharmonics.com/document/code/format
//
// </copyright>
// <summary>
// OrthogonalComposite.cs file.
// </summary>
//+

namespace Graphs.Core
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Linq.Expressions;
  //+
  
  //++ TODO: there's little in common with base class.
  
  /// <summary>
  /// Directed acyclical graph with intersection discovery.
  /// </summary>
  public class OrthogonalComposite<E> : AbstractComposite<E>
    where E : class
  {
    //+
    //- #Address -//
    /// <summary>
    /// Path from root.
    /// </summary>
    protected ICollection<E> Address { get; set; }
    
    //- #VisitorList -//
    /// <summary>
    /// List of visitors.
    /// </summary>
    protected ICollection<OrthogonalComposite<E>> VisitorList { get; set; }
    
    //+
    //- @Ctor -//
    /// <inheritdoc/>
    public OrthogonalComposite() : this(new List<E>())
    {
    }
    
    /// <summary>
    /// Creates vertex and stores its address path.
    /// </summary>
    /// <param name="address"></param>
    protected OrthogonalComposite(ICollection<E> address) : base()
    {
      VisitorList=new List<OrthogonalComposite<E>>();
      Address=address;
    }
    //+
    //- @Follow -//
    /// <inheritdoc/>
    public override IVertex<E> Follow(E edge)
    {
      OrthogonalComposite<E> node;
      if(EdgeMap.ContainsKey(edge))
      {
        node=EdgeMap[edge] as OrthogonalComposite<E>;
      }
      else
      {
        ICollection<E> address=new List<E>(Address);
        address.Add(edge);
        node=DiscoverIntersection(address);
        if(node==null)
        {
          node=new OrthogonalComposite<E>(address);
        }
        EdgeMap[edge]=node;
      }
      node.Add(this);
      //+
      return node;
    }
    
    //- #Add -//
    /// <summary>
    /// Add visitor to list.
    /// </summary>
    /// <param name="visitor">Visitor.</param>
    protected void Add(OrthogonalComposite<E> visitor)
    {
      if(!VisitorList.Contains(visitor))
      {
        VisitorList.Add(visitor);
      }
    }
    
    //- #IsAt -//
    /// <summary>
    /// Determines whether this node is reachable from given path.
    /// </summary>
    /// <param name="addr">Some path.</param>
    /// <returns>True if reachable, false otherwise.</returns>
    protected bool IsAt(ICollection<E> addr)
    {
      if(addr.Count==Address.Count)
      {
        ICollection<E> addr1=new List<E>(Address);
        foreach (E edge in addr)
        {
          if(!addr1.Remove(edge))
          {
            return false;
          }
        }
        //+
        return true;
      }
      //+
      return false;
    }
    
    //- #Discover -//
    /// <inheritdoc cref="M:Graphs.Core.OrthogonalComposite`1.DiscoverIntersection(System.Collections.Generic.ICollection{`0},System.Int32)"/>
    protected OrthogonalComposite<E> DiscoverIntersection(ICollection<E> addr)
    {
      return DiscoverIntersection(addr,Address.Count);
    }
    
    //++ TODO: Method is too long.
    /// <summary>
    /// Looks for node reachable by path.
    /// </summary>
    /// <param name="addr">Some path.</param>
    /// <param name="fromDepth">Caller's depth.</param>
    /// <returns>Node if found, null otherwise.</returns>
    protected OrthogonalComposite<E> DiscoverIntersection(ICollection<E> addr, int fromDepth)
    {
      OrthogonalComposite<E> discovered=null;
      int depth=Address.Count;

      if(IsAt(addr))
      {
        discovered=this;
      }
      else
      {
        if(depth>0 && depth<=fromDepth)
        {
          //++ TODO: why keep all other visitors?
          discovered=VisitorList.First().DiscoverIntersection(addr,depth);
        }
        else
        {
          if(depth>fromDepth && depth>=addr.Count)
          {
            //+ too deep
          }
          else
          {
            foreach(E edge in EdgeMap.Keys)
            {
              discovered=(EdgeMap[edge] as OrthogonalComposite<E>).DiscoverIntersection(addr,depth);
              if(discovered!=null)
              {
                //+
                break;
              }
            }
          }
        }
      }
      //++ TODO: that's a lot of braces.
      //+
      return discovered;
    }
  }
}
